import { uploadParamsType } from '../type';
import { httpRequest } from '../utils/httpRequest';

export function upload(
  instance: any,
  params: uploadParamsType,
  bucketName?: string
): Promise<any> {
  return new Promise((resolve, reject) => {
    const file = params.file;
    if (!file) return reject('未提供上传文件');

    const { maxSize = -1, partSize = -1, queueLength = 1 } = params;

    // 是否采用分片
    const isPart = maxSize > 0 && file.size > maxSize;

    // 非切片
    if (!isPart) {
      let Abort: any = {};
      Abort.cancel = function () {
        Abort.instance?.abort();
      };
      params.onCancel && params.onCancel(Abort);
      instance
        .getSignedUrlPromise('putObject', {
          Bucket: bucketName,
          Key: params.fileName,
          Expires: 60 * 60 * 24,
        })
        .then((url: string) => {
          httpRequest({
            url,
            method: 'put',
            data: file,
            xhrInstance: Abort,
            onProgress: params.onUploadProgress,
          })
            .then((res: any) => {
              resolve(res);
            })
            .catch((err: any) => {
              reject(err);
            });
        })
        .catch((err: any) => {
          reject(err);
        });
    }

    // 切片
    if (isPart) {
      // 获取分片上传id
      instance
        .createMultipartUpload({
          Bucket: bucketName,
          Key: params.fileName,
        })
        .promise()
        .then((result: any) => {
          const uploadId = result.UploadId;

          // 统一 cancel 方法中断切片上传
          let Abort: any = {};
          Abort.cancel = function () {
            // 中断s3上传
            instance.abortMultipartUpload({
              Bucket: bucketName,
              Key: params.fileName,
              UploadId: uploadId,
            });
          };
          params.onCancel && params.onCancel(Abort);

          // 所有切片临时存储
          let completeParts: any[] = [];
          // 开始切片
          const chunks = Math.ceil(file.size / partSize);
          let uploadList: Array<any> = [];

          // 组装切片数据
          for (let i = 1; i <= chunks; i++) {
            const start = (i - 1) * partSize;
            const end = Math.min(start + partSize, file.size) - 1;

            uploadList.push({
              PartNumber: i,
              Body: file.slice(start, end + 1),
            });
          }

          // 上传
          function upload(item: any): Promise<any> {
            return new Promise((resolve, reject) => {
              instance
                .uploadPart({
                  Bucket: bucketName,
                  Key: params.fileName,
                  UploadId: uploadId,
                  PartNumber: item.PartNumber,
                  Body: item.Body,
                })
                .promise()
                .then((data: any) => {
                  completeParts.push({
                    PartNumber: item.PartNumber,
                    ETag: data.ETag,
                  });
                  resolve(null);
                })
                .catch((err: any) => {
                  reject(err);
                });
            });
          }

          // 队列上传
          for (let i: number = 0; i < queueLength; i++) {
            uploadList[i].uploading = true;
            loop(uploadList[i]);
          }

          // 加入队列
          function loop(item: any) {
            upload(item)
              .then(() => {
                item.uploaded = true;
                let uploadedLen: number = uploadList.filter(
                  (var0: any) => var0.uploaded
                ).length;
                // 按照切片完成数量来返回进度
                params.onUploadProgress &&
                  params.onUploadProgress({
                    loaded: Math.floor((uploadedLen / chunks) * file.size),
                    total: file.size,
                  });
                // 找出未开始上传的文件
                let index = uploadList.findIndex(
                  (item) => !item.uploading && !item.uploaded
                );
                if (index > -1) {
                  uploadList[index].uploading = true;
                  loop(uploadList[index]);
                }
                // 全部上传完毕，开始合并
                if (uploadList.every((var0: any) => var0.uploaded)) {
                  // 按照切片文件排序 ，避免合并后文件错误
                  const finalParts = completeParts.sort(
                    (a, b) => a.PartNumber - b.PartNumber
                  );
                  instance
                    .completeMultipartUpload({
                      Bucket: bucketName,
                      UploadId: uploadId,
                      MultipartUpload: {
                        Parts: finalParts,
                      },
                      Key: params.fileName,
                    })
                    .promise()
                    .then((error: any) => {
                      resolve(error);
                    })
                    .catch((err: any) => {
                      reject(err);
                    });
                }
              })
              .catch((err) => {
                reject(err);
              });
          }
        })
        .catch((err: any) => {
          reject(err);
        });
    }
  });
}
